#ifdef PSOS
namespace std { }

using namespace std;

#include <string>
#include <locale>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <string.h>


#include "memfs.h"
/* This file implements MemElement, Mfs and Mshell */

//#define DEBUG_SEARCH

                  /* MemElement implementation */

MemElement::MemElement()
  /* 
   * constructor: init this memory element as a root.
   */
{
  dirContent=0;
  previousElement=0;
  nextElement=0;
  upDir=0;
  entryContent=0;
  elementName=new char[2];
  strcpy(elementName,"/");
}

MemElement::~MemElement()
  /* 
   * Destructor: free all subelements within the element
   * and remove this element from the tree
   */
{
  //Delete the subelements
  while (dirContent) 
    delete dirContent;
  
  //Remove this element from the tree, by adapting all pointer to it

  if (previousElement)
  {
    //Previous element should point to this's next element
    previousElement->nextElement=nextElement;
  }  
  else
  {
    //This is the first element. If in a dir, the nextelement becomes the
    //first element
    if (upDir) upDir->dirContent = nextElement;
  }
  if (nextElement) 
  {
    //NextElement should point to this's previous element
    nextElement->previousElement=previousElement;
  }

  //Finally, delete the entries content and the element's name
  if (entryContent) delete []entryContent;
  if (elementName) delete[] elementName;
}

                     /* Mfs implementation */

Mfs::Mfs()
  /* 
   * constructor: initialise this filesystem
   */
{
  root = new MemElement;
}

Mfs::~Mfs() 
  /* Destructor */
{
  delete root;
}


int Mfs::GetContent(const char *entrypath, char** content)
  /* 
   * Get the string content of entrypath (a full path)
   * returns an error if element could not be found
   */
{
  MemElement* loper;
  // Try to select entry entrypath
  int errCode = SelectElement(&loper,entrypath,false,false,
			      false);
  if (errCode) return errCode;
  //get the content
  *content = loper->entryContent;
  return Mfs::MDM_NO_ERROR;
}


int Instr(int startpos, const char *haystack, const char *needle)
  /*
   * returns the position of needle in haystack, starting at startpos.
   * returns -1 if not found
   */
{
  const char *ptr = strstr(&haystack[startpos],needle);
  if (ptr)
    return(ptr-haystack);
  else
    return(-1);
}

int Mfs::MkDir(const char *dirpath)
  /*
   * make the directory dirpath. This path can be absolute and relative 
   * returns NO_ROOT. If no root is present
   */
{
  MemElement* loper;
  return(SelectElement(&loper,dirpath,true,true,false));
}

int Mfs::SelectElement(MemElement* *element, const char *dirpath, bool isDir)
  /*
   * element should be a pointer to an existing pointer!!
   *
   * Select element in dirpath. selects only elements of correct type (isDir)
   * returns an error if the path is not found.
   *
   * Calls the internal recursive routine to do this
   */
{
  return SelectElement(element, dirpath, isDir, false, false);
}

int Mfs::SelectElement(MemElement* *element, const char *dirpath, 
				 bool isDir, bool createIfNecessary, 
				 bool onlyName)
  /* This function is only for internal use within the Mfs class
   *
   * element should be a pointer to an existing pointer!!
   *
   * if onlyName==true, the *element variable is used as current directory, 
   * so it has to be valid
   * if onlyName==false, the whole path is validated ( and created first 
   * if necessary and createIfNecessay==true)
   */
{
  MemElement* currentDir = *element;
  bool newElement = false;
  char subdir[100];
  if (!onlyName)
  {
    //We start at the root
    currentDir = root;
    
    int errCode;
    
    // Parse the dirpath. Make each dir in the path if it is not present
    int startpos = 1;  //skip the root's '/'
    int pos=Instr(startpos,dirpath,"/");
    
    // Walk recursively to the correct directory if dirs are found in dirpath
    while(pos!=-1)
    {
      //Dive into this subdir
      strncpy(subdir,&dirpath[startpos],pos-startpos);
      subdir[pos-startpos]='\0';
      errCode = SelectElement(&currentDir,subdir,true,createIfNecessary,true);
      if (errCode) return errCode;
      startpos = pos+1;
      pos = Instr(startpos , dirpath, "/");
    }

    //Copy the name of the desired dir to subdir
    strcpy(subdir,&dirpath[startpos]);
  }
  else
  {
    //Only a name
    strcpy(subdir,dirpath);
  }

  //We are in the correct directory, try to find the desired element.
  //If not found, make the element after all elements
  MemElement* loper = currentDir;
  if (strcmp(subdir,".")==0)
  {
    // stay at current dir
  }
  else if (strcmp(subdir,"..")==0)
  {
    // go one dir up.
    if (loper->upDir) loper=loper->upDir;
  }
  else if (strlen(subdir)>0)
  {
    if (loper->dirContent)
    {
      //Directory already has elements
      loper = loper->dirContent;
      MemElement* previousElement = loper;
      while (loper)
      {
        //compare element-name with subdir, iff element has correct type
        if ( (loper->entryContent && isDir==false) ||
	   (!loper->entryContent && isDir==true) )
	  if (strcasecmp(loper->elementName,subdir)==0) break;
      
        previousElement = loper;
        loper = loper->nextElement;
      }
      if (!loper)
      {
        if (!createIfNecessary) return Mfs::MDM_ELEMENT_NOT_FOUND;
        newElement = true;
        loper = previousElement;
        loper->nextElement= new MemElement();
        loper->nextElement->previousElement = loper;
        loper->nextElement->upDir = loper->upDir;
        loper=loper->nextElement;
      }
    }
    else
    {
      //This directory is still empty, create first element
      newElement = true;
      if (!createIfNecessary) return Mfs::MDM_ELEMENT_NOT_FOUND;
      loper->dirContent = new MemElement();
      loper->dirContent->upDir = loper;
      loper=loper->dirContent;
    }
    if (newElement)
    {
      delete[] loper->elementName;
      loper->elementName = new char[strlen(subdir)+1];
      strcpy(loper->elementName, subdir);
    }
    if (!loper->entryContent && !isDir)
    {
      loper->entryContent = new char[1];
      strcpy(loper->entryContent, "");
    }
  }
  *element = loper;
  return Mfs::MDM_NO_ERROR;
}
  
void Mfs::AddUpDirToPath(char *path, MemElement* loper)
  /*
   * Recursive routine to build a path to loper
   * - first go to the parent dir upDir
   * - second, add the elementName to $path
   */
{
  if (loper->upDir)
  {
    AddUpDirToPath(path,loper->upDir);
    //This is not the root, so add '/{name}' to the path
    strcat(path,"/");
    strcat(path,loper->elementName);
  } 
  else
  {
    //The root, remove entire path, '/' will be done later
    strcpy(path,"");
  }
}

void Mfs::MakePath(char *path, MemElement* loper)
  /* 
   * Compose the path to/including element loper
   * by a recursive algorithm
   */
{
  AddUpDirToPath(path,loper);
  if (path[0] != '/') 
  {
    //loper is the root, so init path
    strcpy(path,"/");
  }
}

int Mfs::MkEntry(const char *entrypath, double content)
  /*
   * Make an entry (make all dirs if necessary) and put the content in it.
   */
{
  MemElement* loper;
  //Create the entry.
  int errCode = SelectElement(&loper,entrypath,false,true,false);
  if (errCode) return errCode;

  //Fill the entry's content
  char buf[100];
  sprintf(buf,"%.16g",content);
  if (loper->entryContent) delete[] loper->entryContent;
  loper->entryContent = new char[strlen(buf)+1];
  strcpy(loper->entryContent,buf);

  return Mfs::MDM_NO_ERROR;
}

int Mfs::MkEntry(const char *entrypath, char* content)
  /*
   * Make an entry (make all dirs if necessary) and put the content in it.
   */
{
  MemElement* loper;
  //Create the entry.
  int errCode = SelectElement(&loper,entrypath,false,true,false);
  if (errCode) return errCode;

  //Fill the entry's content
  if (loper->entryContent) delete[] loper->entryContent;
  loper->entryContent = new char[strlen(content)+1];
  strcpy(loper->entryContent,content);

  return Mfs::MDM_NO_ERROR;
}

int Mfs::RmEntry(const char *entrypath)
  /*
   * Remove the entry
   */
{
  MemElement* loper;
  int errCode = SelectElement(&loper,entrypath,false,false,false);
  if (errCode) return errCode;

  delete loper;
  return 0;
}

int Mfs::RmDir(const char *dirpath)
  /*
   * Remove the directory
   */
{
  MemElement* loper;
  int errCode = SelectElement(&loper,dirpath,true,false,false);
  if (errCode) return errCode;

  if (loper==root)
  {
    root = new MemElement;
  }  
  delete loper;
  return 0;
}


                  /* Mshell class implementation */

Mshell::Mshell(Mfs &filesystem, const char *newhomedir)
{
  char fullpath[1024];
  fs = &filesystem;
  homedir = new char[1];
  strcpy(homedir, "");

  GetAbsolutePath(fullpath, newhomedir);

  delete[] homedir;
  homedir = new char[strlen(fullpath)+1];
  strcpy(homedir, fullpath);

  strcpy(currentPath,homedir);

  for (int i=0; i<ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS; i++) foundElements[i] = 0;
  numberOfFoundElements = currentElement = 0;
}

Mshell::Mshell(Mshell &parentshell, const char *newhomedir)
{
  fs = parentshell.fs;

  char fullpath[1024];
  parentshell.GetAbsolutePath(fullpath, newhomedir);
  
  homedir = new char[strlen(fullpath)+1];
  strcpy(homedir, fullpath);
  strcpy(currentPath,homedir);

  for (int i=0; i<ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS; i++) foundElements[i] = 0;
  numberOfFoundElements = currentElement = 0;
}

Mshell::~Mshell()
{
  for (int i=0; i<ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS; i++) if (foundElements[i]) delete[] foundElements[i];
  delete[] homedir;
}

int Mshell::MkDir(const char *dirpath)
  /*
   * make the directory dirpath. This path can be absolute and relative 
   *
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, dirpath);
  return fs->MkDir(fullpath);
}

int Mshell::ChDir(const char *dirpath)
  /* Change directory to desired path. dirpath can be absolute and relative
   * returns MDM_NO_ERROR if succesfull, and an error-code otherwise
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, dirpath);

  MemElement* loper;
  int errCode = fs->SelectElement(&loper,fullpath,true);
  if (errCode) return errCode;

  //loper points to directory, but we need the path, so compose the path
  fs->MakePath(currentPath, loper);
  return Mfs::MDM_NO_ERROR;
}

int Mshell::RmEntry(const char *entrypath)
  /*
   * Remove the entry
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, entrypath);
  return fs->RmEntry(fullpath);
}

int Mshell::RmDir(const char *dirpath)
  /*
   * Remove the directory
   */
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, dirpath);
  return fs->RmDir(fullpath);
}

int Mshell::GetAbsolutePath(char *fullpath, const char* dirpath)
  /* 
   * Convert the possible relative path dirpath to a absolute one
   * The function copies the path to abspath, so enough memory has to be
   * available
   *
   * TODO: remove .. and . references
   */
{
  if (dirpath[0] == '/')
  {
    // This is an absolute path already
    // sometimes the absolute path begins with "//", correct that
    if (dirpath[1] == '/')
      strcpy(fullpath, &dirpath[1]);
    else
      strcpy(fullpath, dirpath);
  }
  else if (dirpath[0] == '~')
  {
    //This is relative to the homedir
    //if homedir = "/", skip the homedir
    if (strcmp(homedir, "/")!=0)
      strcpy(fullpath, homedir);
    else
      strcpy(fullpath, "");
    strcat(fullpath, &dirpath[1]);
  }
  else
  {
    //This is a relative path
    //if currentpath = "/", skip the currentpath
    if (strcmp(currentPath, "/")!=0)
      strcpy(fullpath, currentPath);
    else
      strcpy(fullpath, "");
    strcat(fullpath, "/");
    strcat(fullpath, dirpath);
  }
  return Mfs::MDM_NO_ERROR;
}

int Mshell::FindInitDirectoryElements(const char *dirpath, int typeMask)
  /*
   * Initialises a search by setting the findElement to curdir
   * use findNextElement to get the actual elements
   * It only finds elements indicated by typeMask
   */
{
  MemElement* dir;
  MemElement* loper;
  char fullpath[1024];

#ifdef DEBUG_SEARCH
      std::cerr << "Mshell::" << __FUNCTION__ << " : " << dirpath << std::endl;
#endif
  //Delete all entries in the current list
  for (int i=0; i<ARBITRARY_MAXIMUM_DIRECTORY_ELEMENTS; i++)
    if (foundElements[i]) 
    {
	delete[] foundElements[i];
	foundElements[i]=NULL;
    }
  numberOfFoundElements = 0;
  currentElement = 0;
  
  GetAbsolutePath(fullpath, dirpath);
#ifdef DEBUG_SEARCH
      std::cerr << "Mshell::" << __FUNCTION__ << " : abspath = " << fullpath << std::endl;
#endif
  int errCode = fs->SelectElement(&dir,fullpath,true);
  if (errCode) return errCode;

  char elementpath[1024];
  
  loper = dir->dirContent;
  //Add the path of all entries in the directory to the list
  while (loper)
  {
#ifdef DEBUG_SEARCH
      fs->MakePath(elementpath,loper);
      std::cerr << "Mshell::" << __FUNCTION__ << " : loper name = " << elementpath << std::endl;
      std::cerr << "\t entryContent:" << loper->entryContent << "  typeMask: " << typeMask << std::endl;
#endif
    if ( (!loper->entryContent && (typeMask & Mfs::MDM_DIR)) ||
	 (loper->entryContent && (typeMask & Mfs::MDM_ENTRY)) )
    {
      //Add this element to the list
      fs->MakePath(elementpath,loper);
#ifdef DEBUG_SEARCH
      std::cerr << "Mshell::" << __FUNCTION__ << " : adding name = " << elementpath << std::endl;
#endif
      foundElements[numberOfFoundElements] = new char[strlen(elementpath)+1];
      strcpy(foundElements[numberOfFoundElements],elementpath);
      numberOfFoundElements++;
    }
    loper = loper->nextElement;
  }
  return Mfs::MDM_NO_ERROR;  
}

int Mshell::FindGetNextElement(char ** const elementName)
  /*
   * provides the next element name in elementName
   * returns NO_MORE_ELEMENTS *after* the last element
   */
{
  if (currentElement<numberOfFoundElements)
  {
    *elementName = foundElements[currentElement++];
    return Mfs::MDM_NO_ERROR;
  }
  else
  {
    *elementName = 0;
    return Mfs::MDM_NO_MORE_ELEMENTS;
  }
}

int Mshell::FindRewind()
  /*
   * Rewinds the currentElement to the beginning of the list
   */
{
  currentElement = 0;
  return 0;
}

int Mshell::Export(Serialiser &serialiser, const char *exportpath)
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, exportpath);
  MemElement *dir;
  int errCode = fs->SelectElement(&dir,fullpath,true);
  if (errCode) return errCode;

  if (dir->dirContent)
  {
  	MemElement* loper=dir->dirContent;
  	while(loper)
  	{
  		if (!loper->entryContent)
  		{
  			char newpath[1024];
  			serialiser.StartDir(loper->elementName);
  			strcpy(newpath,fullpath);
  			strcat(newpath,"/");
  			strcat(newpath,loper->elementName);
  			Export(serialiser,newpath);
  			serialiser.EndDir(loper->elementName);
  		}
  		else
  		{
  			serialiser.AddEntry(loper->elementName,loper->entryContent);
  		}
  		loper = loper->nextElement;
  	}
  }
  return Mfs::MDM_NO_ERROR;
}


int Mshell::copy(Mshell &sh)
{
  char fullpath[1024];
  strcpy(fullpath, currentPath);
  return(copy(sh,fullpath));
}

int Mshell::copy(Mshell &sh, char *exportpath)
{
  char fullpath[1024];
  GetAbsolutePath(fullpath, exportpath);
  MemElement *dir;
  int errCode = fs->SelectElement(&dir,fullpath,true);
  if (errCode) return errCode;

  if (dir->dirContent)
  {
  	MemElement* loper=dir->dirContent;
  	while(loper)
  	{
  		if (!loper->entryContent)
  		{
  			char newpath[1024];
  			char shpath[1024];
			sh.GetAbsolutePath( shpath, sh.currentPath);
			sh.MkDir(loper->elementName);
			sh.ChDir(loper->elementName);
  			strcpy(newpath,fullpath);
  			strcat(newpath,"/");
  			strcat(newpath,loper->elementName);
  			
			copy(sh,newpath);

  			sh.ChDir(shpath);
  		}
  		else
  		{
			sh.MkEntry(loper->elementName,loper->entryContent);
  		}
  		loper = loper->nextElement;
  	}
  }
  return Mfs::MDM_NO_ERROR;
}

int Mshell::Import(Serialiser &serialiser, const char *importpath)
{
//  std::cerr << "Mshell::" << __FUNCTION__ << " : " << importpath << std::endl;
  return serialiser.Import(*this,importpath);
}

                      /* Serialiser implementation */
Serialiser::Serialiser()
  /* 
   * constructor: this serialiser does nothing.
   * normal serialiser's should have some argument that gives a clue
   * where to store/retrieve the data
  */
{
}

Serialiser::~Serialiser()
  /* 
   * Destructor: normally the file (or something else) would be closed here
   */
{
}

int Serialiser::StartDir(char *dirname)
  /*
   * Called by the shell, to start parsing a directory's elements
   */
{ return 0;}

int Serialiser::EndDir(char *dirname)
  /*
   * Called by the shell, to indicate the end of a directory elements
   */
{ return 0;}

int Serialiser::AddEntry(char *entryname, char *textcontent)
  /*
   * Called by the shell, to add an entry.
   */
{ return 0;}

int Serialiser::Import(Mshell &msh, const char *importpath)
  /*
   * Called by the shell when importing
   * This functions should use the shell to create the internal structure
   */
{ return 0;}


int dummyToFoolCompiler()
{
    double d=0;
    float f=0;
    char *c=0;
    int i=0;
    unsigned int ui=0;
#ifndef PSOS
	long long ll=0;
    unsigned long long ull=0;
#endif
	bool b=0;
    return(1);
    Mfs params_fs;
    Mshell root(params_fs);
    root.SetContent("",d);
    root.SetContent("",f);
    root.SetContent("",i);
    root.SetContent("",c);
    root.SetContent("",b);
    root.SetContent("",ui);
    root.GetContent("",&d);
    root.GetContent("",&f);
    root.GetContent("",&i);
    root.GetContent("",&c);
    root.GetContent("",&b);
    root.GetContent("",&ui);

#ifndef PSOS
    root.GetContent("",&ll);
    root.GetContent("",&ull);
#endif

    root.MkEntry("",d);
    root.MkEntry("",f);
    root.MkEntry("",i);

    root.MkEntry("",c);
    root.MkEntry(c,c);
    root.MkEntry("","");

    root.MkEntry("",b);
    root.MkEntry("",ui);

#ifndef PSOS
    root.MkEntry("",ll);
    root.MkEntry("",ull);
#endif

	return 0;
}

static int dummyInt = dummyToFoolCompiler();

#ifdef TEST
/* testprogram */
#include "XMLParser.h"

int main(int argc, char* argv[])
{
  char* test;
  Mfs params_fs;
  Mshell root(params_fs);

  float i=5;
  test = "fout";
  root.MkDir("~/settings/cameras");
  Mshell params(root, "/settings/cameras");
  params.MkDir("~/1/wrl2im");
  params.ChDir("~/1/wrl2im");
  params.MkEntry("fx",166);
  params.MkEntry("fy",166);
  params.MkEntry("sk",1.4);
  params.MkEntry("u0",157);
  params.MkEntry("v0",109.75);
  params.MkEntry("k1",-0.024);
  params.MkEntry("k2",-0.00069);
  params.MkEntry("Rx",1);
  params.MkEntry("Ry",2);
  params.MkEntry("Rz",3);
  params.MkEntry("Tx",4);
  params.MkEntry("Ty",5);
  params.MkEntry("Tz",6);

  params.MkDir("~/1/im2wrl");
  params.ChDir("~/1/im2wrl");
  params.MkEntry("fx",-166);
  params.MkEntry("fy",166);
  params.MkEntry("sk",1.4);
  params.MkEntry("u0",157);
  params.MkEntry("v0",109.75);
  params.MkEntry("k1",-0.024);
  params.MkEntry("k2",-0.00069);
  params.MkEntry("Rx",1);
  params.MkEntry("Ry",2);
  params.MkEntry("Rz",3);
  params.MkEntry("Tx",4);
  params.MkEntry("Ty",5);
  params.MkEntry("Tz",6);

  params.MkDir("~/2/wrl2im");
  params.ChDir("~/2/wrl2im");
  params.MkEntry("fx",266);
  params.MkEntry("fy",166);
  params.MkEntry("sk",1.4);
  params.MkEntry("u0",157);
  params.MkEntry("v0",109.75);
  params.MkEntry("k1",-0.024);
  params.MkEntry("k2",-0.00069);
  params.MkEntry("Rx",1);
  params.MkEntry("Ry",2);
  params.MkEntry("Rz",3);
  params.MkEntry("Tx",4);
  params.MkEntry("Ty",5);
  params.MkEntry("Tz",6);

  params.MkDir("~/2/im2wrl");
  params.ChDir("~/2/im2wrl");
  params.MkEntry("fx",-266);
  params.MkEntry("fy",166);
  params.MkEntry("sk",1.4);
  params.MkEntry("u0",157);
  params.MkEntry("v0",109.75);
  params.MkEntry("k1",-0.024);
  params.MkEntry("k2",-0.00069);
  params.MkEntry("Rx",1);
  params.MkEntry("Ry",2);
  params.MkEntry("Rz",3);
  params.MkEntry("Tx",4);
  params.MkEntry("Ty",5);
  params.MkEntry("Tz",6);
  params.SetContent("/test",test);
  {
    ofstream myfile("/home/jurjen/xmltest");
    if (myfile.is_open())
    {
      XMLParser myparser(myfile);
      //myparser.StartDir("settings");
      params.Export(myparser,"~");
      //myparser.EndDir("settings");
    }
  }
  {
    ofstream myfile("/home/jurjen/xmltestall");
    if (myfile.is_open())
    {
      XMLParser myparser(myfile);
      //myparser.StartDir("settings");
      params.Export(myparser,"/");
      //myparser.EndDir("settings");
    }
  }
  {
    ifstream myfile("/home/jurjen/xmltest");
    if (myfile.is_open())
    {
      XMLParser myparser(myfile);
      params.RmDir("~");
      params.Import(myparser,"~");
    }
  }
  params.FindInitDirectoryElements("~",Mfs::MDM_DIR);
  {
    ofstream myfile("/home/jurjen/xmltest2");
    if (myfile.is_open())
    {
      XMLParser myparser(myfile);
      //myparser.StartDir("settings");
      params.Export(myparser,"~");
      //myparser.EndDir("settings");
    }
  }
  return 0;
}
#endif




























































